/*
 *  ckr_spec_spec_runner_specs.cpp
 *  ckrSpec
 *
 *  Created by C. Keith Ray on 2006.11.06.
 *  Copyright 2006 __MyCompanyName__. All rights reserved.
 *
 */

#import "ckr_spec_cpp_specs.h"
#import "ckr_spec_cpp.h"

#include <iostream>
#include <sstream>
#include <vector>
#include <deque>

#include <assert.h>

using namespace std;
using namespace ckr;

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

static void Spec_runner_retains_added_contexts();
static void Spec_runner_runs_added_contexts();
static void Global_spec_runner_gets_contexts_from_statics();
static void Macros_allow_context_to_be_shared_by_others();

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void run_spec_runner_specs()
{
	Spec_runner_retains_added_contexts();
	Spec_runner_runs_added_contexts();
	Global_spec_runner_gets_contexts_from_statics();
	Macros_allow_context_to_be_shared_by_others();
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

DECLARE_SPEC(AnEmptySpec);

void AnEmptySpec::spec()
{
};

static void Spec_runner_retains_added_contexts()
{
	SpecRunner trunner;
	AnEmptySpec aspec;
	
	assert( trunner.numSpecs() == 0 );

	trunner.add( & aspec );
	
	assert( trunner.numSpecs() == 1 );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class ARunnableSpec : public ckr::Context
{
public:
	bool hasBeenRun;

	ARunnableSpec()
		: Context( "ARunnableSpec", __LINE__, __FILE__ )
		, hasBeenRun( false )
	{ 
	};

	virtual void spec()
	{
		hasBeenRun = true;
	}
};

static void Spec_runner_runs_added_contexts()
{
	SpecRunner trunner;
	ARunnableSpec aspec1;
	ARunnableSpec aspec2;
	SpecListener listener;

	trunner.add( & aspec1 ).add( & aspec2 );
	trunner.runAll( & listener );
	
	assert( aspec1.hasBeenRun );
	assert( aspec2.hasBeenRun );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

static SpecRunner aFileLocalSpecRunner;

DECLARE_SPEC(AStaticEmptySpec);

IMPLEMENT_SPEC_WITH_RUNNER(AStaticEmptySpec,&aFileLocalSpecRunner)
{
	FAIL("AStaticEmptySpec executed");
}

static void Global_spec_runner_gets_contexts_from_statics()
{
	RecordingFailuresSpecListener listener;
	SpecRunner* tr = &aFileLocalSpecRunner;
	
	assert( tr != NULL );
	assert( tr->numSpecs() > 0 );
	tr->runAll( & listener );
	
	std::string actual = listener.logStr();

	const std::string expected("AStaticEmptySpec executed FAILED in AStaticEmptySpec; line");

	std::string::size_type foundPos = actual.find( expected, 0 );
	assert( foundPos != std::string::npos );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

static SpecRunner anotherFileLocalSpecRunner;

BEGIN_CONTEXT(An_int_in_a_common_context)

	int commonIntValue;

	virtual void setup()
	{
		commonIntValue = 1;
	}
	virtual void teardown()
	{
		commonIntValue = 0;
	}

END_CONTEXT;

SPECIFY_WITH_RUNNER(An_int_in_a_common_context,Should_equal_3_but_not_really,
	&anotherFileLocalSpecRunner)
{
	VALUE(commonIntValue).SHOULD_EQUAL( 3 ); // provoke a fail msg
	commonIntValue = 2;
	// FYI we don't share an instance of the base "context", each 
	// spec is its own instance; we're actually sharing the setup 
	// and teardown via subclassing.
}

SPECIFY_WITH_RUNNER(An_int_in_a_common_context,Should_equal_5_but_not_really,
	&anotherFileLocalSpecRunner)
{
	VALUE(commonIntValue).SHOULD_EQUAL( 5 ); // provoke a fail msg
}

static void Macros_allow_context_to_be_shared_by_others()
{
	RecordingFailuresSpecListener listener;
	SpecRunner* tr = &anotherFileLocalSpecRunner;
	
	tr->runAll( & listener );
	
	std::string actual = listener.logStr();

	{
		const std::string expected1("commonIntValue (1) should equal 3 (3) FAILED in Should_equal_3_but_not_really; line");

		std::string::size_type foundPos1 = actual.find( expected1, 0 );
		assert( foundPos1 != std::string::npos );
	}
	{
		const std::string expected2("commonIntValue (1) should equal 5 (5) FAILED in Should_equal_5_but_not_really; line");

		std::string::size_type foundPos2 = actual.find( expected2, 0 );
		assert( foundPos2 != std::string::npos );
	}
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

